﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using MatrixCalculator;

namespace ObjectBuilder
{
    /// <summary>
    /// Třída načte informace z Assembly a zpracuje je do Objeků typu Item.
    /// </summary>
    public class Parser : ObjectBuilder.IParser
    {
        #region privateVariables
        private string assembly;
        private Type[] typeArray;
        private Item[] itemArray;
        private ICollection<Item> interfaces;
        private double[,] gen, asc;
        private int i = 0, j = 0;
        #endregion
        #region property
        /// <summary>
        /// Matice Generalizace 
        /// </summary>
        public double[,] Gen
        {
            get { return this.gen; }
        }
        /// <summary>
        /// Matice Asociace
        /// </summary>
        public double[,] Asc
        {
            get { return this.asc; }
        }
        /// <summary>
        /// Jmeno/cesta k souboru s Assembly
        /// </summary>
        public string Assembly
        {
            get { return this.assembly; }
            set { this.assembly = value; }
        }
        public Item[] ItemArray { get { return this.itemArray; } }
        #endregion

        private Calculator mc = new Calculator();
        public Parser()
        {
        }
        /// <summary>
        /// Konstruktor
        /// </summary>
        /// <param name="assembly">jmeno assembly</param>
        public Parser(string assembly)
        {
            this.assembly = assembly;
        }
        /// <summary>
        /// Načte Asssembly ze zadaného souboru a rozdělí ji na Itemy. Item Reprezentuje jednotlivé třídy a uchovává o nich informace.
        /// Indexy interfacu a rodičovských tříd.
        /// </summary>
        public void parse()
        {
            // Načte assemlby do proměnné "a", přečte z ní všecny třídy a uloží je do pole "typeArray" typu Type
            System.Reflection.Assembly a = System.Reflection.Assembly.LoadFrom(assembly);
            typeArray = a.GetTypes();
            itemArray = new Item[typeArray.Length];
            // Pole Interfacu nachazejících se v Assembly
            interfaces = new List<Item>();
            // Najde v Assembly všechny Interface
            foreach (Type item in typeArray)
            {
                if (item.IsInterface)
                {
                    Item it = new Item();
                    it.Name_ = item.Namespace;
                    it.Name = item.Name;
                    it.ClassIndex = i;
                    interfaces.Add(it);
                }
                i++;
            }
            i = 0;
            foreach (Type item in typeArray)
            {
                Item it = new Item();
                it.ClassIndex = i;
                it.Name_ = item.Namespace;
                it.Name = item.Name;
                // pokud je třída potomkem jiné třídy z assembly, do proměnné ParrentIndex uloží ClassIndex rodičovské třídy
                foreach (Type sub in typeArray)
                {
                    if (item.IsSubclassOf(sub))
                    {
                        it.ParentIndex = j;
                    }
                    j++;
                }
                j = 0;
                // Načte všechny Interface danné třídy
                Type[] interfaces1 = item.GetInterfaces();
                if (interfaces1.Count() > 0 && interfaces.Count > 0)
                {
                    bool c = false;
                    foreach (Type tmp in interfaces1)
                    {
                        foreach (Item tmpItem in interfaces)
                        {
                            // Pokud třída implementuje Inteface obsažený v Asssembly do InterfaceIndexu uloží ClassIndex danného Interface
                            if (tmpItem.compareTo(tmp.Name, tmp.Namespace))
                            {
                                it.InterfaceIndex = tmpItem.ClassIndex;
                                c = true;
                                break;
                            }
                        }
                        if (c) break;
                    }
                }

                itemArray[i] = it;
                i++;
            }
            i = 0;
            foreach (var item in typeArray)
            {
                Association(item, itemArray.ToList<Item>());
                i++;
            }
        }

        private void Association(Type item, List<Item> _it)
        {
            FieldInfo[] pi = item.GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public); 
            foreach (var it in pi)
            {
                //Console.WriteLine("{0}",it.FieldType.Name);
                if (it.FieldType.IsGenericType)
                {
                    //Console.WriteLine(it.FieldType.GetGenericArguments);
                    string s = it.FieldType.GetGenericArguments()[0].Name;
                    Console.WriteLine(s);
                    if (isClass(s))
                    _it.FindAll(
                       x => x.Name.Equals(item.Name)).First<Item>().setBinding(
                       _it.FindAll(
                           x => x.Name.Equals(s)).First<Item>().ClassIndex);
                }
                if (isClass(it.FieldType.Name))
                {
                    //Console.WriteLine("name: {0}\n FieldType: {1} , {2}", it.Name, it.FieldType.Name, it.DeclaringType);
                    _it.FindAll(
                        x => x.Name.Equals(item.Name)).First<Item>().setBinding(
                        _it.FindAll(
                            x => x.Name.Equals(it.FieldType.Name)).First<Item>().ClassIndex);
                }
            }

        }

        private bool isClass(string name)
        {
            foreach (Item it in itemArray)
            {
                if (it.Name == name) return true;
            }
            return false;
        }
        
        /// <summary>
        ///  Sestavi matici sousednosti
        /// </summary>
        public void buildMatrix()
        {
            this.gen = new double[typeArray.Length, typeArray.Length];
            gen = mc.fill(0, gen);
            this.asc = new double[typeArray.Length, typeArray.Length];
            asc = mc.fill(0, asc);
            int i = 0;
            foreach (Item item in itemArray)
            {
                if (item.ParentIndex != -1)
                {
                    gen[item.ClassIndex, item.ParentIndex] = 1;
                }else
                if (item.InterfaceIndex != -1)
                {
                    gen[item.ClassIndex, item.InterfaceIndex] = 1;
                }
                for (int j = 0; j < asc.GetLength(0); j++)
                {
                    if (item.Association[j] > -1)
                    {
                        asc[item.ClassIndex, item.Association[j]] = 1;
                    }
                }
                i++;
            }
        }
        /// <summary>
        /// Vytiskne matici
        /// </summary>
        /// <returns> string s vypisem matice </returns>
        public string ToString(double[,] matrix, bool k)
        {
            StringBuilder sb = new StringBuilder();
            //sb.Append(matrix.GetLength(1));
            for (int i = -2; i < matrix.GetLength(1); i++)
            {
                if (i < 0) sb.Append("   ");
                for (int j = -2; j < matrix.GetLength(1); j++)
                {
                    if (j > 0 && i >= 0)
                    {
                        if (matrix[i, j] == 1)
                        {
                            sb.Append("  " + matrix[i, j].ToString());
                        }
                        else
                        {
                            sb.Append("   ");
                        }
                    }

                    if (j == 0 && i >= 0)
                    {
                        if (matrix[i, j] == 1)
                        {
                            if (i < 10) sb.Append("0" + i + "| " + matrix[i, j].ToString());
                            else sb.Append(i + "| " + matrix[i, j].ToString());
                        }
                        else
                        {
                            if (i < 10) sb.Append("0" + i + "|  ");
                            else sb.Append(i + "|  ");
                        }
                    }
                    if (i < 0 && j >= matrix.GetLength(1) - 2) break;
                    if (i == -2)
                    {
                        if (j < 8) sb.Append("0" + (j + 2).ToString() + " ");
                        else sb.Append((j + 2).ToString() + " ");
                    }
                    if (i == -1) sb.Append("===");
                }
                sb.Append("\n");
            }
            return sb.ToString();
        }

        public string ToString(double[,] matrix)
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < matrix.GetLength(1); i++)
            {
                sb.Append(ItemArray[i].Name+ "  ");
                for (int j = 0; j < matrix.GetLength(1); j++)
                {
                    sb.Append("  " + matrix[i, j]);
                }
                sb.Append("\n");
            }
            return sb.ToString();
        }
        /// <summary>
        /// Metoda vypise nazvy trid a jejich index v matici sousednosti
        /// </summary>
        private void method()
        {
            foreach (Item item in itemArray)
            {
                Console.WriteLine("{0} : {1}", item.Name, item.ClassIndex);
            }
        }

    }
}
